/**
 * 
 */
package it.unical.canonaco.rende.igpe.game.cbv.logiclayer.engine.component.filehandlers;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.EOFException;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.Serializable;
import java.io.UTFDataFormatException;

import it.unical.canonaco.rende.igpe.game.cbv.logiclayer.engine.component.world.Chessboard;
import it.unical.canonaco.rende.igpe.game.cbv.logiclayer.engine.component.world.Map;
import it.unical.canonaco.rende.igpe.game.cbv.logiclayer.engine.component.world.MapFileManager;
import it.unical.canonaco.rende.igpe.game.cbv.logiclayer.engine.component.world.TextureInfoBean;
import it.unical.canonaco.rende.igpe.game.cbv.logiclayer.engine.exceptions.InvalidFileException;

/**
 * @author Massimo Canonaco
 *
 */
public class CBVMap implements MapFileManager {
	
	
	/** */
	private static final String EXTENSION = ".cbvmap";
	
	public CBVMap()
	{
	}
	
	/* (non-Javadoc)
	 * @see it.unical.canonaco.rende.igpe.game.cbv.logiclayer.engine.component.world.MapFileManager#build(java.lang.String, java.lang.String)
	 */
	@Override
	public Map build(String source, String fileName) throws FileNotFoundException, IOException, InvalidFileException{
		Map result = new Map();
		if(!fileName.endsWith(EXTENSION))
			fileName = fileName.concat(EXTENSION);
		
		DataInputStream iStream = new DataInputStream(new FileInputStream(source+File.separatorChar+fileName));

		try{
			InfoMapLump map = new InfoMapLump();
			map.read(iStream);
			result.setName(map.name);
			result.setSalt(map.salt);
			
			for(int index = 0; index < map.totalTextures; index++)
			{
				result.addTexture(new TextureInfoBean(iStream.readUTF(),iStream.readUTF(),iStream.readUTF(),iStream.readInt()), false);
			}
			
			Chessboard chess = new Chessboard(0, map.size);
			result.setArena(chess);

			for(int i = 0; i < map.size; i++)
				for(int j = 0; j < map.size; j++)
				{
					chess.setWalkable(i, j, iStream.readBoolean());
					if(iStream.readBoolean())
						chess.addSpawnCells(i, j);
					result.setTextureToField(i, j, iStream.readInt());
				}
			
		// If we reach the end of file in an unexpected way that means some parameters/bytes are missing so the file isn't correct.
		// If some part of the file are not written in UTFData so the file isn't correct
		}catch(EOFException | UTFDataFormatException e){
			throw new InvalidFileException();
		}finally{
			iStream.close();
		}
		return result;
	}

	/* (non-Javadoc)
	 * @see it.unical.canonaco.rende.igpe.game.cbv.logiclayer.engine.component.world.MapFileManager#save(java.lang.String, java.lang.String, it.unical.canonaco.rende.igpe.game.cbv.logiclayer.engine.component.world.Map)
	 */
	@Override
	public void save(String destination, String fileName, Map map) {
		try {
			if(!fileName.endsWith(EXTENSION))
				fileName = fileName.concat(EXTENSION);
			
			DataOutputStream oStream = new DataOutputStream(new FileOutputStream(new File(destination+File.separatorChar+fileName)));
			final int numberOfTexture = map.getTextures().size();
			
			InfoMapLump mapLump = new InfoMapLump(map.getSalt(),map.getName(),numberOfTexture,map.getArena().getSize());
			mapLump.write(oStream);
			
			java.util.Map<Integer, TextureInfoBean> textures = map.getTextures();
			for (TextureInfoBean tex : textures.values()) 
			{
				oStream.writeUTF(tex.getFolder());
				oStream.writeUTF(tex.getName());
				oStream.writeUTF(tex.getExtension());
				oStream.writeInt(tex.getAcutalKey());
			}
			
			Chessboard arena = map.getArena();
			int arenaSize = arena.getSize();
			for(int rowIndex = 0; rowIndex < arenaSize; rowIndex++)
			{
				for(int columnIndex = 0; columnIndex < arenaSize; columnIndex++)
				{
					oStream.writeBoolean(arena.isWalkable(rowIndex, columnIndex));
					oStream.writeBoolean(arena.isSpawnable(rowIndex, columnIndex));
					oStream.writeInt(arena.getTexture(rowIndex, columnIndex).getAcutalKey());
				}
			}
			
			oStream.close();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}

	/** */
	private static class InfoMapLump implements Serializable
	{
		private static final long serialVersionUID = -8985894142775399857L;
		
		private String salt;
		private String name;
		private int totalTextures;
		private int size;	
		
		private InfoMapLump(String salt, String name, int totalTextures, int size)
		{
			this.salt = salt;
			this.name = name;
			this.totalTextures = totalTextures;
			this.size = size;
		}
		
		public InfoMapLump() {}

		private void write(DataOutputStream stream) throws IOException
		{
			stream.writeUTF(salt);
			stream.writeUTF(name);
			stream.writeInt(totalTextures);
			stream.writeInt(size);
		}
		
		private void read(DataInputStream stream) throws IOException
		{
			salt = stream.readUTF();
			name = stream.readUTF();
			totalTextures = stream.readInt();
			size = stream.readInt();
		}
	}

	@Override
	public String getExtension() {
		return EXTENSION;
	}		
}
